home *** CD-ROM | disk | FTP | other *** search
/ GameStar 2004 April / Gamestar_61_2004-04_dvdb.iso / DVDStar / Editace / hltp.exe / {app} / Applications / QuArK / plugins / mapthinfaces.py < prev    next >
Text File  |  2004-01-05  |  9KB  |  308 lines

  1. """   QuArK  -  Quake Army Knife
  2.  
  3. Python code to find thin faces
  4. """
  5. #
  6. # Copyright (C) 1996-99 Armin Rigo
  7. # THIS FILE IS PROTECTED BY THE GNU GENERAL PUBLIC LICENCE
  8. # FOUND IN FILE "COPYING.TXT"
  9. #
  10.  
  11. #$Header: /cvsroot/quark/runtime/plugins/mapthinfaces.py,v 1.6 2003/12/18 21:51:46 peter-b Exp $
  12.  
  13. Info = {
  14.    "plug-in":       "Thin Face Finder",
  15.    "desc":          "Thin Face Finder.",
  16.    "date":          "19 Feb 2001",
  17.    "author":        "tiglari",
  18.    "author e-mail": "tiglari@planetquake.com",
  19.    "quark":         "Version 6.3" }
  20.  
  21.  
  22. from quarkpy.maputils import *
  23. import quarkpy.mapmenus
  24. import quarkpy.mapcommands
  25. import quarkpy.dlgclasses
  26. import mapmadsel
  27. import quarkx
  28.  
  29. #
  30. # A `Live Edit' dialog.  Note the action buttons, which
  31. #   use a rather convoluted technique to produce their
  32. #   effects (when a button is bushed,
  33. #   quarkpy.qmacro.MACRO_fixview is executed with an index
  34. #   saying which, this calls the appropriate function of the
  35. #   dialog, which was attached to the editor when executed).
  36. #
  37. # A ListBox would be better than a ComboBox for data entry,
  38. #   but FormCFG.pas doesn't at the moment support ListBoxes.
  39. #   In principle, this could be fixed.
  40. #
  41.  
  42. class ThinFaceDlg (quarkpy.dlgclasses.LiveEditDlg):
  43.     #
  44.     # dialog layout
  45.     #
  46.  
  47.     endcolor = AQUA
  48.     size = (220,160)
  49.     dfsep = 0.35
  50.  
  51.     dlgdef = """
  52.         {
  53.         Style = "9"
  54.         Caption = "Thin Face Finder"
  55.  
  56.         useless: = {
  57.           Typ = "C"
  58.           Txt = "Brushes:"
  59.           Items = "%s"
  60.           Values = "%s"
  61.           Hint = "These are the brushes that have thin faces.  Pick one," $0D " then push buttons on row below for action."
  62.         }
  63.  
  64.           
  65.         sep: = { Typ="S" Txt=""}
  66.  
  67.         buttons: = {
  68.         Typ = "PM"
  69.         Num = "2"
  70.         Macro = "fixview"
  71.         Caps = "IF"
  72.         Txt = "Actions:"
  73.         Hint1 = "Inspect the chosen one"
  74.         Hint2 = "Fix the chosen one"
  75.         }
  76.  
  77.         num: = {
  78.           Typ = "EF1"
  79.           Txt = "# found"
  80.         }
  81.  
  82.         thin: = {
  83.           Typ = "EF001"
  84.           Txt = "too thin: "
  85.           Hint = "Faces that aren't wider than this are too thin."
  86.         }
  87.         
  88.         sep: = { Typ="S" Txt=""}
  89.  
  90.         exit:py = {Txt="" }
  91.     }
  92.     """
  93.  
  94.     def inspect(self):
  95.         index = eval(self.chosen)
  96.         #
  97.         # FIXME: dumb hack, revise mapmadsel
  98.         #
  99.         m = qmenu.item("",None)
  100.         m.object=self.pack.useless[index]
  101.         mapmadsel.ZoomToMe(m)
  102.         mapmadsel.SelectMe(m)
  103.         thinones = hasThinFaces(m.object,eval(self.pack.thin),retvals=1)
  104.         self.pack.thinfaces=thinones
  105.         self.editor.layout.explorer.sellist=thinones
  106.  
  107.     def fix(self):
  108.         index = eval(self.chosen)
  109.         undo=quarkx.action()
  110.         remains=0
  111.         brush = self.pack.useless[index]
  112.         for thin in self.pack.thinfaces:
  113.             if thin.parent is brush:
  114.                undo.exchange(thin,None)
  115.             else:
  116.                remains=1
  117.         if remains:
  118.             quarkx.msgbox("Some thin faces weren't removed because they are shared",
  119.               MT_INFORMATION, MB_OI)
  120.         self.editor.ok(undo,'delete thin faces')
  121.         if remains==0:
  122.             self.pack.useless.remove(brush)   
  123.         self.src["useless"]=''
  124.         #
  125.         # This seems to need to be called to get the dialog
  126.         #   to reset itself with the new data (not quite sure
  127.         #   why it doesn't happen automatically here, but it
  128.         #   dosnt seem to)
  129.         #
  130.         self.datachange(self.dlg)
  131.  
  132.     def zapall(self):
  133.         undo=quarkx.action()
  134.         for brush in self.pack.useless:
  135.             undo.exchange(brush,None)
  136.         self.editor.ok(undo,'delete microbrushes')
  137.         self.src["useless"]=''
  138.         self.datachange(self.dlg)
  139.  
  140. #
  141. # Define the fixview macro here, put the definition into
  142. #  quarkpy.qmacro, which is where macros called from delphi
  143. #  live.
  144. #
  145. def macro_fixview(self, index=0):
  146.     editor = mapeditor()
  147.     if editor is None: return
  148.     if index==1:
  149.         editor.uselessfacedlg.inspect()
  150.     elif index==2:
  151.         editor.uselessfacedlg.fix()
  152.     #
  153.     # probably won't use this
  154.     #
  155.     elif index==3:
  156.         editor.uselessfacedlg.zapall()
  157.         
  158. quarkpy.qmacro.MACRO_fixview = macro_fixview
  159.  
  160. def hasThinFaces(brush, factor=1.0, retvals=0):
  161.     vals = []
  162.     for face in brush.faces:
  163.         vertices = face.verticesof(brush)
  164.         point = vertices[0]
  165.         norm = (vertices[1]-point).normalized
  166.         for vertex in vertices[2:]:
  167.             perp = perptonormthru(vertex,point,norm)
  168.             if abs(perp)>=factor:
  169.                 break
  170.         #
  171.         # if the loop finishes without a break, then all of
  172.         #  the perps are too small and the face is suspect
  173.         #
  174.         else:
  175.             if retvals:
  176.                 vals.append(face)
  177.             else:
  178.                return 1
  179.     if retvals:
  180.         return vals
  181.     else:
  182.        return 0 
  183.                 
  184.  
  185. def getThin(thin, editor):
  186.     useless = []
  187.     for brush in editor.Root.findallsubitems("",":p"):
  188.         if hasThinFaces(brush,thin):
  189.             useless.append(brush)
  190.     return useless
  191.     
  192. def thinClick(m):
  193.     editor=mapeditor()
  194.     useless=[]
  195.  
  196.     thin = quarkx.setupsubset(SS_MAP, "Options")["thinfacesize"]
  197.     if thin==None:
  198.         thin="1.0"
  199.  
  200.     useless=getThin(eval(thin),editor)    
  201.     
  202.     #
  203.     # Here we start the Live Edit dialog invocation sequence.
  204.     #  Data to be tracked during the life of the dialog goes
  205.     #  here.
  206.     #
  207.     class pack:
  208.         "stick stuff in this"
  209.     pack.useless=useless
  210.     pack.thin=thin
  211.     pack.seen = 0
  212.       
  213.     #
  214.     # This loads the relevant data into the dialog, gets
  215.     #  recalled after changes.
  216.     #
  217.     def setup(self, pack=pack, editor=editor):
  218.         self.pack=pack
  219.         #
  220.         # Part of the convolution for the buttons, to communicate
  221.         #  which objects methods should be called when one pushed.
  222.         # Cleaned up in onclosing below.
  223.         #
  224.         editor.uselessfacedlg=self
  225.         
  226.         #
  227.         # Names and list-indexes of thin brushes
  228.         #
  229.         ran = range(len(pack.useless))
  230.         pack.slist = map(lambda obj, d:"%d) %s"%(d+1, obj.shortname), pack.useless, ran)
  231.         pack.klist = map(lambda d:`d`, ran)
  232.  
  233.         self.src["useless$Items"] = "\015".join(pack.slist)
  234.         self.src["useless$Values"] = "\015".join(pack.klist)
  235.         #
  236.         # load the first value
  237.         #
  238.         if (not pack.seen) and len(ran)>0:
  239.             self.src["useless"] = '0'
  240.             self.chosen = '0'
  241.             pack.seen = 1
  242.         elif len(ran)==0:
  243.             self.src["useless"] = ''
  244.             self.chosen = ''
  245.         #
  246.         # Note the commas, EF..1 controls take 1-tuples as data
  247.         #
  248.         self.src["num"]=len(pack.klist),
  249.         self.src["thin"]=eval(pack.thin),
  250.  
  251.     #
  252.     # When data is entered, this gets executed.
  253.     #
  254.     def action(self, pack=pack, editor=editor):
  255.        src = self.src
  256.        #
  257.        # note what's been chosen
  258.        #
  259.        self.chosen = src["useless"]
  260.        #
  261.        # see if thinness threshold has been changed
  262.        #
  263.        newthin, = self.src["thin"]
  264.        if newthin!=pack.thin:
  265.            if newthin==1.0:
  266.                quarkx.setupsubset(SS_MAP, "Options")["thinfacesize"]=None
  267.            else:
  268.                quarkx.setupsubset(SS_MAP, "Options")["thinfacesize"]="%f2"%newthin
  269.            pack.useless=getThin(newthin, editor)
  270.            pack.thin="%.2f"%newthin
  271.            
  272.     #
  273.     # Cleanup when dialog closes (not needed if no mess has
  274.     #  been created)
  275.     #
  276.     def onclosing(self,editor=editor):
  277.         del editor.uselessfacedlg
  278.         
  279.     #
  280.     # And here's the invocation. 2nd arg is a label for storing
  281.     #  position info in setup.qrk.
  282.     #
  283.     ThinFaceDlg(quarkx.clickform, 'thinface', editor, setup, action, onclosing)
  284.  
  285.  
  286. quarkpy.mapsearch.items.append(qmenu.item('Find &Thin Faces', thinClick,
  287.   "|Find Thin Faces:\n\nThis function will search for and identifies brushes with faces that are suspiciously thin.", "intro.mapeditor.menu.html#searchmenu"))
  288.  
  289. #$Log: mapthinfaces.py,v $
  290. #Revision 1.6  2003/12/18 21:51:46  peter-b
  291. #Removed reliance on external string library from Python scripts (second try ;-)
  292. #
  293. #Revision 1.5  2003/03/21 05:47:45  cdunde
  294. #Update infobase and add links
  295. #
  296. #Revision 1.4  2002/05/21 07:06:10  tiglari
  297. #fix problems with selection dialog (loadiing first selection when appropriate)
  298. #
  299. #Revision 1.3  2001/06/17 21:10:56  tiglari
  300. #fix button captions
  301. #
  302. #Revision 1.2  2001/06/16 03:19:47  tiglari
  303. #add Txt="" to separators that need it
  304. #
  305. #Revision 1.1  2001/05/21 11:58:26  tiglari
  306. #kickoff
  307. #
  308.